#include "StdAfx.h"
#include "FlatCylinder.h"

#include <math.h>


FlatCylinder::FlatCylinder(const Ray& ray, double length, double radius, const Color& color) : _startPoint(ray.P), _direction(ray.D * length)
{
	_radius = radius;
	_radiusInv = 1 / radius;
	_squaredRadiusInv = _radiusInv * _radiusInv;
	_directionLenghtInv = 1 / (length * length);
	_color = color;
}

void FlatCylinder::WhereIntersects(const Ray& ray, FirstIntersectionResult& result)
{
	Point PminusStart = ray.P - _startPoint;
	Point A =  - (_direction * (_direction.ScalarProduct(ray.D) * _directionLenghtInv) - ray.D );
	Point B = PminusStart -  _direction * (_direction.ScalarProduct(PminusStart) * _directionLenghtInv);

	double AtimesB = A.ScalarProduct(B);
	double AtimesA = A.ScalarProduct(A);
	double delta = AtimesB * AtimesB - AtimesA * (B.ScalarProduct(B) - _radius * _radius);

	if (delta < 0)
	{
		result.Set(this);
		return;
	}

	double deltaSqrt = sqrt(delta);
	double x = (-AtimesB - deltaSqrt) / AtimesA;
	Point V = ray.D * x + ray.P;

	double t = _direction.ScalarProduct(V - _startPoint) * _directionLenghtInv;

	if (t >= 0 && t <= 1 && x > 0)
	{
		result.Set(x, this);
		result.ExtraInfoDouble = t;
		return;
	}
	
	x = (-AtimesB + deltaSqrt) / AtimesA;
	V = ray.D * x + ray.P;

	t = _direction.ScalarProduct(V - _startPoint) * _directionLenghtInv;
	if (t >= 0 && t <= 1 && x > 0)
	{			
		result.Set(x, this);
		result.ExtraInfoDouble = t;
		return;
	}
	
	result.Set(this);
}

void FlatCylinder::ColorAtIntersection(SecondIntersectionResult& result, const Point&, const Ray&)
{
	result.ColorAtPoint = _color;
}

Direction FlatCylinder::NormalAtPoint(FirstIntersectionResult& result, const Point& point)
{
	return Direction(point - _startPoint - _direction * result.ExtraInfoDouble);
}

FlatCylinder::~FlatCylinder()
{}
